package com.gitee.wsl.logger

import com.gitee.wsl.LibConfig
import com.gitee.wsl.logger.printer.Empty

//package me.lwb.logger


fun LibConfig.logger(block:Logger.()->Unit){
    Logger.block()
}

/**
 * 日志工具类
 * 1.Logger 日志门面，主要提供便捷的方法
 * 2.LogPrinter 日志输出，主要完成各个输出接口的适配
 * 3.LoggerFactory 日志工厂，主要用于生成子Logger
 */

var globalLogLevel :LogLevel = LogLevel.VERBOSE

expect val platformLogPainter :LogPrinter

/**
 * 基础日志记录器
 */
open class Logger(
    var tag: String = "Logger",
    var level: LogLevel = LogLevel.VERBOSE,
    var logPrinter: LogPrinter = platformLogPainter
    ) {

    val isDebug:Boolean get() = level == LogLevel.DEBUG

    inline fun v(block: () -> Any?) =
        log(LogLevel.VERBOSE, null, block)

    inline fun d(block: () -> Any?) =
        log(LogLevel.DEBUG, null, block)

    inline fun i(block: () -> Any?) =
        log(LogLevel.INFO, null, block)

    inline fun w(throwable: Throwable? = null, block: () -> Any?) =
        log(LogLevel.WARNING, throwable, block)

    inline fun e(throwable: Throwable? = null, block: () -> Any?) =
        log(LogLevel.ERROR, throwable, block)

    inline fun wtf(throwable: Throwable? = null, block: () -> Any?) =
        log(LogLevel.WTF, throwable, block)


    fun v(message: Any?) =
        log(LogLevel.VERBOSE, message, null)

    fun d(message: Any?) =
        log(LogLevel.DEBUG, message, null)

    fun i(message: Any?) =
        log(LogLevel.INFO, message, null)


    fun w(message: Any?, throwable: Throwable? = null) =
        log(LogLevel.WARNING, message, throwable)


    fun e(message: Any?, throwable: Throwable? = null) =
        log(LogLevel.ERROR, message, throwable)


    fun wtf(message: Any?, throwable: Throwable? = null) =
        log(LogLevel.WTF, message, throwable)


    open fun log(
        level: LogLevel,
        message: Any?,
        throwable: Throwable? = null
    ) {
        if ( this.level <= level &&  globalLogLevel <= level) {
            logPrinter.log(level, this.tag, message, throwable)
        }
    }

    inline fun log(
        level: LogLevel,
        throwable: Throwable? = null,
        block: () -> Any?
    ) {
        if (this.level <= level) {
            log(level, block(), throwable)
        }
    }

    /**
     * Writes log with provided level and tag.
     * @param level Log level
     * @param tag Tag of the log record. Nullable
     * @param throwable Throwable to be written into log
     * @param message Message to be written into log
     */
    fun log(
        level: LogLevel,
        tag: String? = null,
        throwable: Throwable? = null,
        message: (() -> String)? = null){

        if ( this.level <= level &&  globalLogLevel <= level) {
            logPrinter.log(level, this.tag + tag, message?.invoke(), throwable)
        }

    }

    /**
     * 生产子Logger的方法，必要时可以重新设置
     */
    var loggerFactory: (childTag: String) -> Logger = ::defaultLoggerFactory

    /**
     * 创建子Logger
     * @param subTag 次级tag，一般为模块名
     */
    operator fun get(subTag: String) = loggerFactory(subTag)

    fun get(subTag: String,level: LogLevel) = get(subTag).apply { this.level = level }

    companion object INSTANCE : Logger(tag = "Logger")
}

inline fun <reified T> T.getLogger() = Logger[T::class.simpleName?:""]

inline fun <reified T> T.getLogger(level: LogLevel) = Logger(T::class.simpleName?:"",level)

inline fun <reified T> Logger.getLogger() = this[T::class.simpleName?:""]

inline fun <reified T> Logger.getLogger(level: LogLevel) = get(T::class.simpleName?:"",level)


internal fun Logger.defaultLoggerFactory(subTag: String) = Logger("$tag-$subTag", level, logPrinter)

fun Logger.printer(printer: LogPrinter){
    this.logPrinter = printer
}

fun Logger.disableLogging():LogPrinter {
    val oldPrinter = logPrinter
    logPrinter = LogPrinter.Empty
    return oldPrinter
}


/**
 * 拷贝
 */
fun Logger.copy(
    tag: String = this.tag,
    level: LogLevel = this.level,
    logPrinter: LogPrinter = this.logPrinter,
    loggerFactory: (childTag: String) -> Logger = ::defaultLoggerFactory,
) = Logger(tag, level, logPrinter).also { it.loggerFactory = loggerFactory }

//fun Any.loggerForClass() = Logger[javaClass.simpleName]


// DEBUG //////////////////////////////////////////////////////////////

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.debug(tag: String?, message: String) {
    log(level = LogLevel.DEBUG, tag = tag) { message }
}

/**
 * Writes log with DEBUG log level and empty tag.
 * @param message Message to be written into log.
 */
fun Logger.debug(message: String) {
    d(message)
}

/**
 * Log exception with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.debug(tag: String? = null, throwable: Throwable) {
    log(LogLevel.DEBUG, tag, throwable, null)
}

/**
 * Log exception with DEBUG log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.debug(throwable: Throwable) = log(LogLevel.DEBUG, throwable = throwable, message = null)

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.debug(tag: String?, message: () -> String) = log(LogLevel.DEBUG, tag, message = message)

/**
 * Writes log with DEBUG log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.debug(message: () -> String) = log(LogLevel.DEBUG, message = message)

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.debug(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.DEBUG, tag, throwable, message)

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.d(tag: String?, message: String) {
    log(LogLevel.DEBUG, tag) { message }
}

/**
 * Writes log with DEBUG log level and empty tag.
 * @param message Message to be written into log.
 */
//fun Logger.d(message: String) {
//    log(LogLevel.DEBUG) { message }
//}

/**
 * Log exception with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.d(tag: String? = null, throwable: Throwable) {
    log(LogLevel.DEBUG, tag, throwable, null)
}

/**
 * Log exception with DEBUG log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.d(throwable: Throwable) = log(LogLevel.DEBUG, throwable = throwable, message = null)

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.d(tag: String?, message: () -> String) = log(LogLevel.DEBUG, tag, message = message)

/**
 * Writes log with DEBUG log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.d(message: () -> String) = log(LogLevel.DEBUG, message = message)

/**
 * Writes log with DEBUG log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.d(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.DEBUG, tag, throwable, message)

// INFO /////////////////////////////////////////////////////////////////

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.info(tag: String?, message: String) {
    log(LogLevel.INFO, tag) { message }
}

/**
 * Writes log with INFO log level and empty tag.
 * @param message Message to be written into log.
 */
fun Logger.info(message: String) {
    i(message)
}

/**
 * Log exception with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.info(tag: String? = null, throwable: Throwable) {
    log(LogLevel.INFO, tag, throwable, null)
}

/**
 * Log exception with INFO log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.info(throwable: Throwable) = log(LogLevel.INFO, throwable = throwable, message = null)

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.info(tag: String?, message: () -> String) = log(LogLevel.INFO, tag, message = message)

/**
 * Writes log with INFO log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.info(message: () -> String) = log(LogLevel.INFO, message = message)

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.info(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.INFO, tag, throwable, message)

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.i(tag: String?, message: String) {
    log(LogLevel.INFO, tag) { message }
}

/**
 * Writes log with INFO log level and empty tag.
 * @param message Message to be written into log.
 */
//fun Logger.i(message: String) {
//    log(LogLevel.INFO) { message }
//}

/**
 * Log exception with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.i(tag: String? = null, throwable: Throwable) {
    log(LogLevel.INFO, tag, throwable, null)
}

/**
 * Log exception with INFO log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
//fun Logger.i(throwable: Throwable) = log(LogLevel.INFO, throwable = throwable, message = null)

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.i(tag: String?, message: () -> String) = log(LogLevel.INFO, tag, message = message)

/**
 * Writes log with INFO log level and empty tag.
 * @param message Function that returns message to be written into log
 */
//fun Logger.i(message: () -> String) = log(LogLevel.INFO, message = message)

/**
 * Writes log with INFO log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.i(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.INFO, tag, throwable, message)

// WARN /////////////////////////////////////////////////////////////////

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.warn(tag: String?, message: String) {
    log(LogLevel.WARNING, tag) { message }
}

/**
 * Writes log with WARN log level and empty tag.
 * @param message Message to be written into log.
 */
fun Logger.warn(message: String) {
    w(message)
    //log(LogLevel.WARNING) { message }
}

/**
 * Log exception with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.warn(tag: String? = null, throwable: Throwable) {
    log(LogLevel.WARNING, tag, throwable, null)
}

/**
 * Log exception with WARN log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.warn(throwable: Throwable) = log(LogLevel.WARNING, throwable = throwable, message = null)

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.warn(tag: String?, message: () -> String) = log(LogLevel.WARNING, tag, message = message)

/**
 * Writes log with WARN log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.warn(message: () -> String) = log(LogLevel.WARNING, message = message)

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.warn(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.WARNING, tag, throwable, message)

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.w(tag: String?, message: String) {
    log(LogLevel.WARNING, tag) { message }
}

/**
 * Writes log with WARN log level and empty tag.
 * @param message Message to be written into log.
 */
//fun Logger.w(message: String) {
//    log(LogLevel.WARNING) { message }
//}

/**
 * Log exception with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.w(tag: String?, message:String?=null,throwable: Throwable) {
    log(LogLevel.WARNING, tag, throwable, null)
}

/**
 * Log exception with WARN log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.w(throwable: Throwable) = log(LogLevel.WARNING, throwable = throwable, message = null)

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.w(tag: String?, message: () -> String) = log(LogLevel.WARNING, tag, message = message)

/**
 * Writes log with WARN log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.w(message: () -> String) = log(LogLevel.WARNING, message = message)

/**
 * Writes log with WARN log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.w(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.WARNING, tag, throwable, message)

// ERROR ////////////////////////////////////////////////////////////////

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.error(tag: String?, message: String) {
    log(LogLevel.ERROR, tag) { message }
}

/**
 * Writes log with ERROR log level and empty tag.
 * @param message Message to be written into log.
 */
fun Logger.error(message: String) {
    e(message)
    //log(LogLevel.ERROR) { message }
}

/**
 * Log exception with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.error(tag: String? = null, throwable: Throwable) {
    log(LogLevel.ERROR, tag, throwable, null)
}

/**
 * Log exception with ERROR log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.error(throwable: Throwable) = log(LogLevel.ERROR, throwable = throwable, message = null)

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.error(tag: String?, message: () -> String) = log(LogLevel.ERROR, tag, message = message)

fun Logger.error(throwable: Throwable?, message: () -> String) = log(LogLevel.ERROR, throwable = throwable, message = message)

/**
 * Writes log with ERROR log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.error(message: () -> String) = log(LogLevel.ERROR, message = message)

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.error(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.ERROR, tag, throwable, message)

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Message to be written into log.
 */
fun Logger.e(tag: String?, message: String) {
    log(LogLevel.ERROR, tag) { message }
}

/**
 * Writes log with ERROR log level and empty tag.
 * @param message Message to be written into log.
 */
//fun Logger.e(message: String) {
//    e(message)
//}

/**
 * Log exception with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.e(tag: String?,message: String? = null, throwable: Throwable) {
    log(LogLevel.ERROR, tag, throwable){ message?:"" }
}

/**
 * Log exception with ERROR log level and empty tag.
 * @param throwable Error or Exception to be written into log.
 */
fun Logger.e(throwable: Throwable) = log(LogLevel.ERROR, throwable = throwable, message = null)

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param message Function that returns message to be written into log
 */
fun Logger.e(tag: String?, message: () -> String) = log(LogLevel.ERROR, tag, message = message)

/**
 * Writes log with ERROR log level and empty tag.
 * @param message Function that returns message to be written into log
 */
fun Logger.e(message: () -> String) = log(LogLevel.ERROR, message = message)

/**
 * Writes log with ERROR log level and provided tag.
 * @param tag Tag of the log record. Optional
 * @param throwable Error or Exception. Optional
 * @param message Message to be written into log.
 */
fun Logger.e(tag: String? = null, throwable: Throwable? = null, message: () -> String) = log(LogLevel.ERROR, tag, throwable, message)
